/* Filename: mytbf.c
 * Description: <TODO>
 * Last modified: humble 20200408 22:06
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include "mytbf.h"

#define MYTBF_MAX (1024)

typedef struct{
    int cps;
    int burst;
    int token;
    int pos;
}mytbf_st;

static mytbf_st *job[MYTBF_MAX];
static int inited = 0;
static struct sigaction alrm_sigaction_save;

static int get_free_pos(void)
{
    int i;
    for(i = 0; i < MYTBF_MAX; i++)
    {
        if(!job[i]){
            return i;
        }
    }
    return -1;
}

static void alrm_sigaction(int s, siginfo_t *infop, void *UNUSED)
{
    int i;

    if(infop->si_code != SI_KERNEL){
        return;
    }

    for(i = 0; i < MYTBF_MAX; i++)
    {
        if(job[i]){
            job[i]->token += job[i]->cps;
            if(job[i]->token > job[i]->burst){
                job[i]->token = job[i]->burst;
            }
        }
    }
}

static void module_unload(void)
{
    int i;

    sigaction(SIGALRM, &alrm_sigaction_save, NULL);

    struct itimerval itv;
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 0;
    if(0 > setitimer(ITIMER_REAL, &itv, NULL)){
        perror("setitimer()");
        exit(1);
    }

    for(i = 0; i < MYTBF_MAX; i++)
    {
        free(job[i]);
    }
}

static void module_load(void)
{
    struct sigaction sa;
    sa.sa_sigaction = alrm_sigaction;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    if(0 > sigaction(SIGALRM, &sa, &alrm_sigaction_save)){
        perror("sigaction()");
        exit(1);
    }

    struct itimerval itv;
    itv.it_interval.tv_sec = 1;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;
    if(0 > setitimer(ITIMER_REAL, &itv, NULL)){
        perror("setitimer()");
        exit(1);
    }
    atexit(module_unload);
}

mytbf_t *mytbf_init(int cps, int burst)
{
    if(cps <= 0 || burst <= 0){
        return NULL;
    }

    if(!inited){
        module_load();
        inited = 1;
    }
    mytbf_st *me = malloc(sizeof(mytbf_st));
    if(!me){
        return NULL;
    }

    int pos = get_free_pos();
    if(pos < 0 || pos >= MYTBF_MAX){
        free(me);
        return NULL;
    }

    me->token = 0;
    me->cps = cps;
    me->burst = burst;
    me->pos = pos;

    job[pos] = me;

    return me;
}

int mytbf_fetch(mytbf_t *ptr, int size)
{
    mytbf_st *me = (mytbf_st *)ptr;

    if((!me) || (size <= 0)){
        return -EINVAL;
    }

    while(me->token <= 0){
        pause();
    }

    int n = me->token < size ? me->token : size;
    me->token -= n;

    return n;
}

int mytbf_return(mytbf_t *ptr, int size)
{
    mytbf_st *me = (mytbf_st *)ptr;
    if((!me) || (size <= 0)){
        return -EINVAL;
    }

    me->token += size;
    if(me->token > me->burst){
        me->token = me->burst;
    }

    return size;
}

int mytbf_destroy(mytbf_t *ptr)
{
    mytbf_st *me = (mytbf_st *)ptr;
    if(!me){
        return -1;
    }
    job[me->pos] = NULL;
    free(me);
    return 0;
}
